/*
 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "wfd_napi.h"
// #include "ability_manager/include/ability_manager_client.h"
#include "ability_manager_client.h"
#include "common/sharing_log.h"
#include "surface_utils.h"
#include "utils.h"
// #include "wfd.h"
#include "wfd_source.h"
#include "wfd_sink.h"
#include "wfd_source_manager.h"
#include "wfd_sourcelistener_manager.h"
#include "wfd_sink_manager.h"
#include "wfd_msg.h"

namespace OHOS {
namespace Sharing {
napi_ref WfdNapi::constructor_ = nullptr;
const std::string BUNDLE_NAME = "com.example.player";
const std::string ABILITY_NAME = "MainAbility";
const std::string CLASS_NAME = "WfdImpl";
const int32_t ARGS_ONE = 1;
const int32_t ARGS_TWO = 2;
const int32_t ARGS_THREE = 3;
// const int32_t ARGS_FOUR = 4;
const int32_t STRING_MAX_SIZE = 255;

WfdNapi::WfdNapi()
{
    SHARING_LOGI("ctor %{public}p.", this);
}

WfdNapi::~WfdNapi()
{
    SHARING_LOGI("dtor %{public}p.", this);
    // CancelCallbackReference();
    nativeWfdSource_.reset();
    nativeWfdSink_.reset();
}

napi_value WfdNapi::Init(napi_env env, napi_value exports)
{
    SHARING_LOGD("trace.");
    napi_property_descriptor staticProperty[] = {
                                                 DECLARE_NAPI_STATIC_FUNCTION("startDiscovery", StartDiscovery),
                                                 DECLARE_NAPI_STATIC_FUNCTION("createCastSession", CreateCastSession),
                                                 DECLARE_NAPI_STATIC_FUNCTION("on", On),
                                                 DECLARE_NAPI_STATIC_FUNCTION("off", Off),
                                                 DECLARE_NAPI_FUNCTION("setDiscoverable", SetDiscoverable)
    };

    napi_property_descriptor properties[] = {
                                             DECLARE_NAPI_FUNCTION("addDevice", AddDevice),
                                             DECLARE_NAPI_FUNCTION("createMirrorPlayer", CreateMirrorPlayer),
                                             DECLARE_NAPI_FUNCTION("acception", Acception)
                                             };
    napi_value constructor = nullptr;
    napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
                                           sizeof(properties) / sizeof(properties[0]), properties, &constructor);

    CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define WfdClient class.");

    status = napi_create_reference(env, constructor, 1, &constructor_);
    CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to create reference of constructor.");

    status = napi_set_named_property(env, exports, CLASS_NAME.c_str(), constructor);
    CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to set constructor.");

    status = napi_define_properties(env, exports, sizeof(staticProperty) / sizeof(staticProperty[0]), staticProperty);
    CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define static function.");

    SHARING_LOGD("success.");
    return exports;
}

void WfdNapi::CancelCallbackReference(const std::string &callbackName)
{
    SHARING_LOGD("trace.");
    std::lock_guard<std::mutex> lock(refMutex_);
    if (jsCallback_ != nullptr) {
        jsCallback_->ClearCallbackReference(callbackName);
    }
}

void WfdNapi::SetCallbackReference(const std::string &callbackName, std::shared_ptr<AutoRef> ref)
{
    SHARING_LOGD("trace.");
    std::lock_guard<std::mutex> lock(refMutex_);
    refMap_[callbackName] = ref;
    if (jsCallback_ != nullptr) {
        jsCallback_->SaveCallbackReference(callbackName, ref);
    }
}

napi_value WfdNapi::Constructor(napi_env env, napi_callback_info info)
{
    SHARING_LOGD("trace.");
    napi_status status;
    napi_value result = nullptr;
    napi_value jsThis = nullptr;

    auto finalize = [](napi_env env, void *data, void *hint) {
        SHARING_LOGD("Destructor in.");
        auto *wfdNapi = reinterpret_cast<WfdNapi *>(data);
        // WfdNapi->CancelCallbackReference();

        delete wfdNapi;
        SHARING_LOGD("Destructor out.");
    };

    status = napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr) {
        napi_get_undefined(env, &result);
        SHARING_LOGE("Failed to retrieve details about the call.");
        return result;
    }

    WfdNapi *jsCast = new (std::nothrow) WfdNapi();
    CHECK_AND_RETURN_RET_LOG(jsCast != nullptr, result, "No memory.");
    jsCast->env_ = env;
    WfdSourceManager& manager = WfdSourceManager::getInstance();
    jsCast->nativeWfdSource_ = manager.getWfdSource();
 
    status = napi_wrap(env, jsThis, reinterpret_cast<void *>(jsCast), finalize, nullptr, nullptr);
    if (status != napi_ok) {
        napi_get_undefined(env, &result);
        delete jsCast;
        SHARING_LOGE("Fail to wrapping js to native napi.");
        return result;
    }

    SHARING_LOGD("success.");
    return jsThis;
}

napi_value WfdNapi::AddDevice(napi_env env, napi_callback_info info)
{
    SHARING_LOGD("trace.");
    napi_value result = nullptr;
    napi_value jsThis = nullptr;
    napi_value args[ARGS_TWO] = {nullptr};
    size_t argCount = ARGS_TWO;

    napi_get_undefined(env, &result);
    auto asyncContext = std::make_unique<WfdAsyncContext>(env);
    asyncContext->asyncWorkType = AsyncWorkType::ASYNC_WORK_ADDDEVICE;

    napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr) {
        SHARING_LOGE("Failed to retrieve details about the call.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Failed to retrieve details about the call");
    }

    napi_valuetype valueType = napi_undefined;
    if (args[0] == nullptr && napi_typeof(env, args[0], &valueType) != napi_ok && valueType != napi_object) {
        SHARING_LOGW("no screenId here.");
    }
    napi_value deviceIdValue;
    napi_value deviceNameValue;
    napi_value ipAdressValue;
    napi_get_named_property(env, args[0], "deviceId", &deviceIdValue);
    napi_get_named_property(env, args[0], "deviceName", &deviceNameValue);
    napi_get_named_property(env, args[0], "ipAddress", &ipAdressValue);
    GetStringFromArgs(env, deviceIdValue, asyncContext->deviceId);
    GetStringFromArgs(env, deviceNameValue, asyncContext->deviceName);
    GetStringFromArgs(env, ipAdressValue, asyncContext->ipAddress);

    napi_valuetype valueTypeArgOne = napi_undefined;
    if (args[1] == nullptr) {
        SHARING_LOGW("no callback here.");
    }
    napi_typeof(env, args[1], &valueTypeArgOne);
    napi_create_reference(env, args[1], 1, &(asyncContext->callbackRef));
    if (valueTypeArgOne != napi_function) {
      SHARING_LOGD("napi_create_promise.");
      napi_create_promise(env, &(asyncContext->deferred), &result);
    } 

    (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncContext->napi));
    CHECK_AND_RETURN_RET_LOG(asyncContext->napi != nullptr, result, "failed to GetJsInstance.");

    napi_value resource = nullptr;
    napi_create_string_utf8(env, "AddDevice", NAPI_AUTO_LENGTH, &resource);

    NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, WfdNapi::AsyncWork, WfdNapi::CompleteCallback,
                                          static_cast<void *>(asyncContext.get()), &asyncContext->work));
    NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));

    asyncContext.release();
    SHARING_LOGD("success.");
    return result;
}

void WfdNapi::CompleteCallback2(napi_env env, napi_status status, void *data) {
    SHARING_LOGD("CompleteCallback2 trace.");
    auto asyncContext = reinterpret_cast<WfdAsyncContext *>(data);
    CHECK_AND_RETURN_LOG(asyncContext != nullptr, "asyncContext is nullptr!");

    if (status != napi_ok) {
        asyncContext->SignError(CommonErrorCode, "napi_create_async_work status != napi_ok");
    }

    napi_value result = nullptr;
    napi_get_undefined(env, &result);

    napi_value args[ARGS_TWO] = {nullptr};
    napi_get_undefined(env, &args[0]);
    napi_get_undefined(env, &args[1]);

    if (asyncContext->errFlag) {
        SHARING_LOGE("async callback failed.");
        CreateError(env, asyncContext->errCode, asyncContext->errMessage, result);
        args[0] = result;
    } else {
        SHARING_LOGD("async callback out.");
        napi_value constructor = nullptr;
        napi_get_reference_value(env, constructor_, &constructor);
        napi_new_instance(env, constructor, 0, nullptr, &result);
        args[1] = result;
    }
    if (asyncContext->deferred) {
        if (asyncContext->errFlag) {
            SHARING_LOGD("napi_reject_deferred.");
            napi_reject_deferred(env, asyncContext->deferred, args[0]);
        } else {
            SHARING_LOGD("napi_resolve_deferred.");
            napi_resolve_deferred(env, asyncContext->deferred, args[1]);
        }
    } else {
        SHARING_LOGD("napi_call_function callback.");
        napi_value callback = nullptr;
        napi_get_reference_value(env, asyncContext->callbackRef, &callback);
        CHECK_AND_RETURN_LOG(callback != nullptr, "callbackRef is nullptr!");

        constexpr size_t argCount = 2;
        napi_value retVal;
        napi_get_undefined(env, &retVal);
        napi_call_function(env, nullptr, callback, argCount, args, &retVal);
        napi_delete_reference(env, asyncContext->callbackRef);
    }

    napi_delete_async_work(env, asyncContext->work);

    if (asyncContext) {
        delete asyncContext;
        asyncContext = nullptr;
    }
    SHARING_LOGD("success.");
}

void WfdNapi::AsyncWork2(napi_env env, void *data)
{
  SHARING_LOGD("AsyncWork2 trace.");
}

napi_value WfdNapi::CreateCastSession(napi_env env, napi_callback_info info)
{
    SHARING_LOGD("trace.");
    napi_value result = nullptr;
    napi_value jsThis = nullptr;
    napi_value args[ARGS_TWO] = {nullptr};
    size_t argCount = ARGS_TWO;

    napi_get_undefined(env, &result);
    auto asyncContext = std::make_unique<WfdAsyncContext>(env);
    asyncContext->asyncWorkType = AsyncWorkType::ASYNC_WORK_CREATCASTSESSION;

    napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr) {
        SHARING_LOGE("Failed to retrieve details about the call.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Failed to retrieve details about the call");
    }

    int32_t endType = 0;
    GetPropertyInt32(env, args[0], "endType", endType);
    if (endType == 0x11) {
      SHARING_LOGW("create source castSession.");
    } else {
      SHARING_LOGW("wrong endType.");
      return nullptr;
    }
     
    napi_valuetype valueType = napi_undefined;
    if (args[1] == nullptr) {
        SHARING_LOGW("no callback here.");
    }
    napi_typeof(env, args[1], &valueType);
    napi_create_reference(env, args[1], 1, &(asyncContext->callbackRef));
    if (valueType != napi_function) {
      SHARING_LOGD("napi_create_promise.");
      napi_create_promise(env, &(asyncContext->deferred), &result);
    } 

    WfdNapi* jsCast = WfdNapi::GetInstance();
    asyncContext->napi = jsCast;
    asyncContext->env = env;

    napi_value resource = nullptr;
    napi_create_string_utf8(env, "CreateCastSession", NAPI_AUTO_LENGTH, &resource);

    NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, WfdNapi::AsyncWork2, WfdNapi::CompleteCallback2,
                                          static_cast<void *>(asyncContext.get()), &asyncContext->work));
    NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));

    asyncContext.release();
    SHARING_LOGD("success.");
    return result;
}

napi_value WfdNapi::StartDiscovery(napi_env env, napi_callback_info info)
{
    SHARING_LOGD("trace.");
    napi_value result = nullptr;
    napi_value jsThis = nullptr;
    napi_value args[ARGS_TWO] = {nullptr};
    size_t argCount = ARGS_TWO;

    napi_get_undefined(env, &result);
    auto asyncContext = std::make_unique<WfdAsyncContext>(env);
    asyncContext->asyncWorkType = AsyncWorkType::ASYNC_WORK_STARTDISCOVERY;

    napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr) {
        SHARING_LOGE("Failed to retrieve details about the call.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Failed to retrieve details about the call");
    }

    bool isArray = false;
    napi_is_array(env, args[0], &isArray);
    if (isArray) {
      uint32_t length = 0;
      napi_get_array_length(env, args[0], &length);
      for (uint32_t i = 0; i < length; i++) {
         napi_value element;
         napi_get_element(env, args[0], i, &element);
         uint32_t protocal = 0;
         napi_get_value_uint32(env, element, &protocal);
         if (protocal == 1) break;
         else SHARING_LOGW("input array should include CAST_PLUS_MIRROR");
      }
    } else {
        SHARING_LOGW("first param should be an array.");
    }
    
    napi_valuetype valueType = napi_undefined;
    if (args[1] == nullptr) {
        SHARING_LOGW("no callback here.");
    }
    napi_typeof(env, args[1], &valueType);
    napi_create_reference(env, args[1], 1, &(asyncContext->callbackRef));
    if (valueType != napi_function) {
      SHARING_LOGD("napi_create_promise.");
      napi_create_promise(env, &(asyncContext->deferred), &result);
    } 
   
    std::shared_ptr<WfdSource> wfdSource = WfdSourceFactory::CreateSource(0, "wfd_Source_demo");

    if (wfdSource == nullptr) {
      SHARING_LOGE("WfdNapi::StartDiscovery CreateSource fail.");
    }

    WfdSourceManager& manager = WfdSourceManager::getInstance();
    manager.getWfdSource() = wfdSource;
   
    WfdNapi* jsCast = WfdNapi::GetInstance();
    jsCast->nativeWfdSource_ = wfdSource;
    asyncContext->napi = jsCast;

    WfdSourceListenerManager& managerListener = WfdSourceListenerManager::getInstance();
    std::shared_ptr<WfdCallbackNapi> jsCallback = managerListener.getWfdSourceListener();
    jsCallback->SetWfdNapi(jsCast);
    if (jsCallback == nullptr) {
      SHARING_LOGW("WfdNapi::StartDiscovery listener jsCallback NULL.");
    }
  
    wfdSource->SetListener(jsCallback);

    napi_value resource = nullptr;
    napi_create_string_utf8(env, "StartDiscovery", NAPI_AUTO_LENGTH, &resource);

    NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, WfdNapi::AsyncWork, WfdNapi::CompleteCallback,
                                          static_cast<void *>(asyncContext.get()), &asyncContext->work));
    NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));

    asyncContext.release();
    SHARING_LOGD("startDiscovery success.");
    return result;
}

napi_value WfdNapi::On(napi_env env, napi_callback_info info)
{
    SHARING_LOGD("trace.");
    napi_value result = nullptr;
    napi_value jsThis = nullptr;
    napi_value args[ARGS_TWO] = {nullptr};
    size_t argCount = ARGS_TWO;
    napi_ref callback = nullptr;

    napi_get_undefined(env, &result);

    napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr || args[0] == nullptr || args[1] == nullptr) {
        SHARING_LOGE("Failed to retrieve details about the callback.");
        return result;
    }

    napi_valuetype valueType0 = napi_undefined;
    napi_valuetype valueType1 = napi_undefined;
    if (napi_typeof(env, args[0], &valueType0) != napi_ok || valueType0 != napi_string ||
        napi_typeof(env, args[1], &valueType1) != napi_ok || valueType1 != napi_function) {
        SHARING_LOGE("error invalid parameter type.");
        return result;
    }

    char callbackName[STRING_MAX_SIZE] = {0};
    size_t length = 0;

    status = napi_get_value_string_utf8(env, args[0], callbackName, STRING_MAX_SIZE, &length);
    CHECK_AND_RETURN_RET_LOG(status == napi_ok && length > 0, result, "failed to get callback name.");

    status = napi_create_reference(env, args[1], 1, &callback);
    CHECK_AND_RETURN_RET_LOG(status == napi_ok && callback != nullptr, result,
                             "failed to creating reference for callback.");

    std::shared_ptr<AutoRef> autoRef = std::make_shared<AutoRef>(env, callback);
    SHARING_LOGI("On callback: %{public}s.", callbackName);

    WfdNapi* jsCast = WfdNapi::GetInstance();
    std::shared_ptr<WfdCallbackNapi> jsCallback = std::make_shared<WfdCallbackNapi>(env);
    WfdSourceListenerManager& managerListener = WfdSourceListenerManager::getInstance();
    managerListener.getWfdSourceListener() = jsCallback;
    if (jsCallback == nullptr) {
      SHARING_LOGE("On std::shared_ptr<WfdCallbackNapi> jsCallback1 is NULL.");
    }

    if (jsCast == nullptr) {
      SHARING_LOGE("On WfdNapi jsCallback is NULL.");
    }
    jsCast->jsCallback_ = jsCallback;
    jsCast->SetCallbackReference(std::string(callbackName), autoRef);
    SHARING_LOGD("success.");
    return result;
}

napi_value WfdNapi::Off(napi_env env, napi_callback_info info)
{
    SHARING_LOGD("trace.");
    napi_value result = nullptr;
    napi_value jsThis = nullptr;
    napi_value args[ARGS_TWO] = {nullptr};
    size_t argCount = ARGS_TWO;
    napi_ref callback = nullptr;

    napi_get_undefined(env, &result);

    napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr || args[0] == nullptr || args[1] == nullptr) {
        SHARING_LOGE("Failed to retrieve details about the callback.");
        return result;
    }

    napi_valuetype valueType0 = napi_undefined;
    napi_valuetype valueType1 = napi_undefined;
    if (napi_typeof(env, args[0], &valueType0) != napi_ok || valueType0 != napi_string ||
        napi_typeof(env, args[1], &valueType1) != napi_ok || valueType1 != napi_function) {
        SHARING_LOGE("error invalid parameter type.");
        return result;
    }

    char callbackName[STRING_MAX_SIZE] = {0};
    size_t length = 0;

    status = napi_get_value_string_utf8(env, args[0], callbackName, STRING_MAX_SIZE, &length);
    CHECK_AND_RETURN_RET_LOG(status == napi_ok && length > 0, result, "failed to get callback name.");

    status = napi_create_reference(env, args[1], 1, &callback);
    CHECK_AND_RETURN_RET_LOG(status == napi_ok && callback != nullptr, result,
                             "failed to creating reference for callback.");

    std::shared_ptr<AutoRef> autoRef = std::make_shared<AutoRef>(env, callback);
    SHARING_LOGI("On callback: %{public}s.", callbackName);
    WfdNapi* jsCast = WfdNapi::GetInstance();
   
    WfdSourceListenerManager& managerListener = WfdSourceListenerManager::getInstance();
    std::shared_ptr<WfdCallbackNapi> jsCallback = managerListener.getWfdSourceListener();
    if (jsCallback == nullptr) {
      SHARING_LOGE("std::shared_ptr<WfdCallbackNapi> jsCallback2 is NULL.");
    }
    if (jsCast == nullptr) {
      SHARING_LOGE("Off jsCast is NULL.");
    }
    jsCast->jsCallback_ = jsCallback;
    jsCast->CancelCallbackReference(std::string(callbackName));
    SHARING_LOGD("success.");
    return result;
}

napi_value WfdNapi::Acception(napi_env env, napi_callback_info info)
{
    SHARING_LOGD("trace.");
    napi_value result = nullptr;
    napi_value jsThis = nullptr;
    napi_value args[ARGS_THREE] = {nullptr}; // 修改为三个参数
    size_t argCount = ARGS_THREE;

    napi_get_undefined(env, &result);
    auto asyncContext = std::make_unique<WfdAsyncContext>(env);
    asyncContext->asyncWorkType = AsyncWorkType::ASYNC_WORK_ACCEPTION;

    napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr) {
        SHARING_LOGE("Failed to retrieve details about the call.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Failed to retrieve details about the call");
    }

    // 获取deviceId参数
    napi_valuetype valueType0 = napi_undefined;
    if (napi_typeof(env, args[0], &valueType0) != napi_ok || valueType0 != napi_string) {
        SHARING_LOGE("Invalid deviceId parameter.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Invalid deviceId parameter.");
    } else {
        GetStringFromArgs(env, args[0], asyncContext->deviceId);
    }

    // 获取accept参数
    napi_valuetype valueType1 = napi_undefined;
    if (napi_typeof(env, args[1], &valueType1) != napi_ok || valueType1 != napi_boolean) {
        SHARING_LOGE("Invalid accept parameter.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Invalid accept parameter.");
    } else {
        napi_get_value_bool(env, args[1], &asyncContext->accept);
    }

    // 获取回调函数或promise
    napi_valuetype valueType2 = napi_undefined;
    if (napi_typeof(env, args[2], &valueType2) != napi_ok) {
        SHARING_LOGE("Invalid callback/promise parameter.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Invalid callback/promise parameter.");
    } else {
        if (valueType2 == napi_function) {
            napi_create_reference(env, args[2], 1, &(asyncContext->callbackRef));
        } else if (valueType2 == napi_object) {
            napi_create_promise(env, &(asyncContext->deferred), &result);
        }
    }

    (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncContext->napi));
    CHECK_AND_RETURN_RET_LOG(asyncContext->napi != nullptr, result, "failed to GetJsInstance.");

    napi_value resource = nullptr;
    napi_create_string_utf8(env, "Acception", NAPI_AUTO_LENGTH, &resource);

    NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, WfdNapi::AsyncWork, WfdNapi::CompleteCallback,
                                          static_cast<void *>(asyncContext.get()), &asyncContext->work));
    NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));

    asyncContext.release();
    SHARING_LOGD("success.");
    return result;
}

napi_value WfdNapi::CreateMirrorPlayer(napi_env env, napi_callback_info info)
{
    // 怎么改成异步返回？？返回的是个啥？  args[0]不知道有什么用 需要传递什么东西
    SHARING_LOGD("trace.");
    napi_value result = nullptr;
    napi_value jsThis = nullptr;
    napi_value args[ARGS_ONE] = {nullptr};
    size_t argCount = ARGS_ONE;

    napi_get_undefined(env, &result);
    auto asyncContext = std::make_unique<WfdAsyncContext>(env);
    asyncContext->asyncWorkType = AsyncWorkType::ASYNC_WORK_CREATEMIRRORPLAYER;

    napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr) {
        SHARING_LOGE("Failed to retrieve details about the call.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Failed to retrieve details about the call");
    }

    // 处理参数回调  callback || promise
    napi_valuetype valueType = napi_undefined;
    if (args[0] == nullptr && napi_typeof(env, args[0], &valueType) != napi_ok && valueType != napi_function) {
        SHARING_LOGW("no callback here.");
    }
    napi_create_reference(env, args[0], 1, &(asyncContext->callbackRef));

    if (asyncContext->callbackRef == nullptr) {
        SHARING_LOGD("napi_create_promise.");
        napi_create_promise(env, &(asyncContext->deferred), &result);
    }

    (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncContext->napi));
    CHECK_AND_RETURN_RET_LOG(asyncContext->napi != nullptr, result, "failed to GetJsInstance.");

    napi_value resource = nullptr;
    napi_create_string_utf8(env, "CreateMirrorPlayer", NAPI_AUTO_LENGTH, &resource);

    NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, WfdNapi::AsyncWork, WfdNapi::CompleteCallback,
                                          static_cast<void *>(asyncContext.get()), &asyncContext->work));
    NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));

    asyncContext.release();
    SHARING_LOGD("success.");
    return result;
}

napi_value WfdNapi::SetDiscoverable(napi_env env, napi_callback_info info)
{
    SHARING_LOGD("trace.");
    napi_value result = nullptr;
    napi_value jsThis = nullptr;
    napi_value args[ARGS_TWO] = {nullptr};
    size_t argCount = ARGS_TWO;

    napi_get_undefined(env, &result);
    auto asyncContext = std::make_unique<WfdAsyncContext>(env);
    asyncContext->asyncWorkType = AsyncWorkType::ASYNC_WORK_SETDISCOVERABLE;

    napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
    if (status != napi_ok || jsThis == nullptr) {
        SHARING_LOGE("Failed to retrieve details about the call.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Failed to retrieve details about the call");
    }

    bool isDiscoverable = false;
    if (!napi_get_value_bool(env, args[0], &isDiscoverable)) {
        SHARING_LOGE("Failed to get isDiscoverable.");
        asyncContext->SignError(SharingErrorCode::ERR_BAD_PARAMETER, "Failed to get isDiscoverable");
    }
    asyncContext->isDiscoverable = isDiscoverable;

    napi_valuetype valueType = napi_undefined;
    if (args[1] == nullptr && napi_typeof(env, args[1], &valueType) != napi_ok && valueType != napi_function) {
        SHARING_LOGW("no callback here.");
    }

    if (args[1] != nullptr) {
      napi_create_reference(env, args[1], 1, &(asyncContext->callbackRef));
    }

    if (asyncContext->callbackRef == nullptr) {
        SHARING_LOGD("napi_create_promise.");
        napi_create_promise(env, &(asyncContext->deferred), &result);
    }

    //先创建createSink 然后setListener
    std::vector<AAFwk::AbilityRunningInfo> outInfo;
    AAFwk::AbilityManagerClient::GetInstance()->GetAbilityRunningInfos(outInfo);

    std::string bundleName_ = "";
    std::string abilityName_ = "";
    std::string localKey_ = "";

    if (outInfo.size() != 1) {
        SHARING_LOGE("get ability size error: %{public}zu.", outInfo.size());
        for (auto &item : outInfo) {
            SHARING_LOGW("get app, bundle: %{public}s, ability: %{public}s.", item.ability.GetBundleName().c_str(),
                         item.ability.GetAbilityName().c_str());
        }
        bundleName_ = BUNDLE_NAME;
        abilityName_ = ABILITY_NAME;
    } else {
        bundleName_ = outInfo[0].ability.GetBundleName();
        abilityName_ = outInfo[0].ability.GetAbilityName();
        SHARING_LOGI("get app, bundle: %{public}s, ability: %{public}s.", bundleName_.c_str(),
                     abilityName_.c_str());
    }

    DmKit::InitDeviceManager();
    if (DmKit::GetTrustedDevicesInfo().size() > 0) {
        for (auto &item : DmKit::GetTrustedDevicesInfo()) {
            SHARING_LOGI("remote trusted device: %{public}s.", item.deviceId);
        }
    }

    RpcKeyParser parser;
    localKey_ = parser.GetRpcKey(bundleName_, abilityName_, DmKit::GetLocalDevicesInfo().deviceId, CLASS_NAME);

    
    std::shared_ptr<WfdSink> wfdSink = WfdSinkFactory::CreateSink(0, localKey_);
    
    // 创建WfdSink的实例并设置到单例中
    WfdSinkManager& manager = WfdSinkManager::getInstance();
    // manager.getWfdSink() = std::make_shared<WfdSink>();
    // 现在WfdSink的实例已经存储在单例中
    manager.getWfdSink() = wfdSink;
    // CHECK_AND_RETURN_RET_LOG(jsCast->nativeWfdSink_ != nullptr, result, "failed to WfdSinkImpl.");

    // setListenner还有点问题 不知道怎么搞？？传的参是个啥子？
    // jsCast->jsCallback_ = std::make_shared<WfdSinkCallbackNapi>(env);
    // jsCast->jsCallback_->SetWfdNapi(jsCast);
    // jsCast->nativeWfdSink_->SetListener(jsCast->jsCallback_);

    // 按理说应该在startDiscovery之后进行setListener
    // 这个地方参数是什么？
    // std::shared_ptr<WfdSinkCallbackNapi> jsCallback = std::make_shared<WfdSinkCallbackNapi>(env);
    // // 对进行监听
    // wfdSink->SetListener(jsCallback);

    // 保存
    WfdNapi* jsCast = WfdNapi::GetInstance();
    jsCast->nativeWfdSink_ = wfdSink;
    asyncContext->napi = jsCast;

    napi_value resource = nullptr;
    napi_create_string_utf8(env, "SetDiscoverable", NAPI_AUTO_LENGTH, &resource);

    NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, WfdNapi::AsyncWork, WfdNapi::CompleteCallback,
                                          static_cast<void *>(asyncContext.get()), &asyncContext->work));
    NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));

    asyncContext.release();
    SHARING_LOGD("success.");
    return result;
}

void WfdNapi::AsyncWork(napi_env env, void *data)
{
    SHARING_LOGD("trace.");
    auto asyncContext = reinterpret_cast<WfdAsyncContext *>(data);
    if (asyncContext == nullptr) {
        SHARING_LOGE("WfdAsyncContext is nullptr.");
        return;
    }

    SHARING_LOGD("workType: %{public}d.", asyncContext->asyncWorkType);
    if (asyncContext->napi == nullptr || asyncContext->napi->nativeWfdSource_ == nullptr) {
        if (asyncContext->napi == nullptr) {
            SHARING_LOGE("WfdAsyncContext->napi is nullptr.");
        } else {
            SHARING_LOGE("WfdAsyncContext->nativeWfdSource_ is nullptr.");
        }

        asyncContext->SignError(CommonErrorCode, "native instance is null");
        return;
    }

    auto jsCast = asyncContext->napi;
    auto nativeWfdSource = jsCast->nativeWfdSource_;
    auto nativeWfdSink = jsCast->nativeWfdSink_;
    int32_t ret = 0;

    switch (asyncContext->asyncWorkType) {
        case AsyncWorkType::ASYNC_WORK_STARTDISCOVERY: {
            if (!asyncContext->errFlag) {
                ret = nativeWfdSource->StartDiscover();
                if (ret < 0) {
                    SHARING_LOGE("nativeWfdSource->Start error, %{public}d.", ret);
                    asyncContext->SignError(CommonErrorCode, "native instance Start error");
                    return;
                }
            } else {
                ret = asyncContext->errCode;
            }
            asyncContext->JsResult = std::make_unique<WfdJsResult>(ret);
            break;
        }
        case AsyncWorkType::ASYNC_WORK_ADDDEVICE: {
            if (!asyncContext->errFlag) {
                WfdCastDeviceInfo wfdCastDeviceInfo;
                wfdCastDeviceInfo.deviceId = asyncContext->deviceId;
                wfdCastDeviceInfo.deviceName = asyncContext->deviceName;
                wfdCastDeviceInfo.ipAddr = asyncContext->ipAddress;
                ret = nativeWfdSource->AddDevice(0, wfdCastDeviceInfo);
                if (ret < 0) {
                    SHARING_LOGE("nativeWfdSource->Stop error, %{public}d.", ret);
                    asyncContext->SignError(CommonErrorCode, "native instance Stop error");
                    return;
                }
            } else {
                ret = asyncContext->errCode;
            }

            asyncContext->JsResult = std::make_unique<WfdJsResult>(ret);
            break;
        }

        case AsyncWorkType::ASYNC_WORK_ACCEPTION: {
            if (!asyncContext->errFlag) {
                std::string deviceId = asyncContext->deviceId;
                bool accept = asyncContext->accept;
                ret = nativeWfdSink->Acception(deviceId, accept);
                if (ret < 0) {
                    SHARING_LOGE("nativeWfdSource->Acception error, %{public}d.", ret);
                    asyncContext->SignError(CommonErrorCode, "native instance Acception error");
                    return;
                }
            } else {
                ret = asyncContext->errCode;
            }
            asyncContext->JsResult = std::make_unique<WfdJsResult>(ret);
            break;
        }

        case AsyncWorkType::ASYNC_WORK_CREATEMIRRORPLAYER: {
            if (!asyncContext->errFlag) {
                napi_value result = nullptr;
                napi_value constructor = nullptr;
                napi_status status = napi_get_reference_value(env, constructor_, &constructor);
                if (status != napi_ok) {
                    SHARING_LOGE("Fail to get the representation of constructor object.");
                    napi_get_undefined(env, &result);
                    return;
                }
                status = napi_new_instance(env, constructor, 0, nullptr, &result);
                if (status != napi_ok) {
                    SHARING_LOGE("Fail to instantiate js cast instance.");
                    napi_get_undefined(env, &result);
                    return;
                }
                // asyncContext->instance = result;
            } else {
                ret = asyncContext->errCode;
                asyncContext->JsResult = std::make_unique<WfdJsResult>(ret);
            }

            break;
        }

        case AsyncWorkType::ASYNC_WORK_SETDISCOVERABLE: {
            if (!asyncContext->errFlag) {
                bool isDiscoverable = asyncContext->isDiscoverable;
                if (isDiscoverable) {
                    ret = nativeWfdSink->Start();
                } else {
                    ret = nativeWfdSink->Stop();
                }
                if (ret < 0) {
                    SHARING_LOGE("nativeWfdSink->SetDiscoverable error, %{public}d.", ret);
                    asyncContext->SignError(CommonErrorCode, "native instance SetDiscoverable error");
                    return;
                }
            } else {
                ret = asyncContext->errCode;
            }

            asyncContext->JsResult = std::make_unique<WfdJsResult>(ret);
            auto *wfdNapi = reinterpret_cast<WfdNapi *>(asyncContext->napi);
            delete wfdNapi;

            break;
        }
        default:
            SHARING_LOGE("error unknown operation (%{public}d).", (int32_t)asyncContext->asyncWorkType);
            break;
    }
    SHARING_LOGD("workType: %{public}d.", asyncContext->asyncWorkType);
    return;
}

void WfdNapi::CompleteCallback(napi_env env, napi_status status, void *data)
{
    SHARING_LOGD("trace.");
    auto asyncContext = reinterpret_cast<WfdAsyncContext *>(data);
    CHECK_AND_RETURN_LOG(asyncContext != nullptr, "asyncContext is nullptr!");

    if (status != napi_ok) {
        asyncContext->SignError(CommonErrorCode, "napi_create_async_work status != napi_ok");
    }

    napi_value result = nullptr;
    napi_get_undefined(env, &result);

    napi_value args[ARGS_TWO] = {nullptr};
    napi_get_undefined(env, &args[0]);
    napi_get_undefined(env, &args[1]);

    if (asyncContext->errFlag) {
        SHARING_LOGE("async callback failed.");
        CreateError(env, asyncContext->errCode, asyncContext->errMessage, result);
        args[0] = result;
    } else {
        SHARING_LOGD("async callback out.");
        if (asyncContext->JsResult != nullptr) {
            auto res = asyncContext->JsResult->GetJsResult(env, result);
            if (res == napi_ok) {
                args[1] = result;
            } else {
                SHARING_LOGE("asyncContext->JsResult->GetJsResult error, %{public}d.", (int32_t)res);
                asyncContext->SignError(CommonErrorCode, "failed to get return data");
                CreateError(env, asyncContext->errCode, asyncContext->errMessage, result);
                args[0] = result;
            }
        }
    }
    if (asyncContext->deferred) {
        if (asyncContext->errFlag) {
            SHARING_LOGD("napi_reject_deferred.");
            napi_reject_deferred(env, asyncContext->deferred, args[0]);
        } else {
            SHARING_LOGD("napi_resolve_deferred.");
            napi_resolve_deferred(env, asyncContext->deferred, args[1]);
        }
    } else {
        SHARING_LOGD("napi_call_function callback.");
        napi_value callback = nullptr;
        napi_get_reference_value(env, asyncContext->callbackRef, &callback);
        CHECK_AND_RETURN_LOG(callback != nullptr, "callbackRef is nullptr!");

        constexpr size_t argCount = 2;
        napi_value retVal;
        napi_get_undefined(env, &retVal);
        napi_call_function(env, nullptr, callback, argCount, args, &retVal);
        napi_delete_reference(env, asyncContext->callbackRef);
    }

    napi_delete_async_work(env, asyncContext->work);

    if (asyncContext) {
        delete asyncContext;
        asyncContext = nullptr;
    }
    SHARING_LOGD("success.");
}

napi_status WfdNapi::CreateError(napi_env env, int32_t errCode, const std::string &errMsg, napi_value &errVal)
{
    SHARING_LOGD("trace.");
    napi_get_undefined(env, &errVal);

    napi_value msgValStr = nullptr;
    napi_status nstatus = napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
    if (nstatus != napi_ok || msgValStr == nullptr) {
        SHARING_LOGE("create error message str fail.");
        return napi_invalid_arg;
    }

    nstatus = napi_create_error(env, nullptr, msgValStr, &errVal);
    if (nstatus != napi_ok || errVal == nullptr) {
        SHARING_LOGE("create error fail.");
        return napi_invalid_arg;
    }

    napi_value codeStr = nullptr;
    nstatus = napi_create_string_utf8(env, "code", NAPI_AUTO_LENGTH, &codeStr);
    if (nstatus != napi_ok || codeStr == nullptr) {
        SHARING_LOGE("create code str fail.");
        return napi_invalid_arg;
    }

    napi_value errCodeVal = nullptr;
    nstatus = napi_create_int32(env, errCode, &errCodeVal);
    if (nstatus != napi_ok || errCodeVal == nullptr) {
        SHARING_LOGE("create error code number val fail.");
        return napi_invalid_arg;
    }

    nstatus = napi_set_property(env, errVal, codeStr, errCodeVal);
    if (nstatus != napi_ok) {
        SHARING_LOGE("set error code property fail.");
        return napi_invalid_arg;
    }

    napi_value msgStr = nullptr;
    nstatus = napi_create_string_utf8(env, "msg", NAPI_AUTO_LENGTH, &msgStr);
    if (nstatus != napi_ok || msgStr == nullptr) {
        SHARING_LOGE("create msg str fail.");
        return napi_invalid_arg;
    }

    nstatus = napi_set_property(env, errVal, msgStr, msgValStr);
    if (nstatus != napi_ok) {
        SHARING_LOGE("set error msg property fail.");
        return napi_invalid_arg;
    }

    napi_value nameStr = nullptr;
    nstatus = napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &nameStr);
    if (nstatus != napi_ok || nameStr == nullptr) {
        SHARING_LOGE("create name str fail.");
        return napi_invalid_arg;
    }

    napi_value errNameVal = nullptr;
    nstatus = napi_create_string_utf8(env, "BusinessError", NAPI_AUTO_LENGTH, &errNameVal);
    if (nstatus != napi_ok || errNameVal == nullptr) {
        SHARING_LOGE("create BusinessError str fail.");
        return napi_invalid_arg;
    }

    nstatus = napi_set_property(env, errVal, nameStr, errNameVal);
    if (nstatus != napi_ok) {
        SHARING_LOGE("set error name property fail.");
        return napi_invalid_arg;
    }

    return napi_ok;
}

bool WfdNapi::GetPropertyInt32(napi_env env, napi_value config, const std::string &type, int32_t &result)
{
    SHARING_LOGD("trace.");
    napi_value item = nullptr;
    bool exist = false;
    napi_status status = napi_has_named_property(env, config, type.c_str(), &exist);
    if (status != napi_ok || !exist) {
        SHARING_LOGE("can not find %{public}s property.", type.c_str());
        return false;
    }

    if (napi_get_named_property(env, config, type.c_str(), &item) != napi_ok) {
        SHARING_LOGE("get %{public}s property fail.", type.c_str());
        return false;
    }

    if (napi_get_value_int32(env, item, &result) != napi_ok) {
        SHARING_LOGE("get %{public}s property value fail.", type.c_str());
        return false;
    }

    return true;
}

bool WfdNapi::GetStringFromArgs(napi_env env, napi_value in, std::string &out)
{
    SHARING_LOGD("trace.");
    napi_valuetype valueType = napi_undefined;
    char buff[STRING_MAX_SIZE] = {0};
    size_t length = 0;
    if (in == nullptr || napi_typeof(env, in, &valueType) != napi_ok || valueType != napi_string) {
        SHARING_LOGE("error Invalid parameter type.");
        return false;
    }

    napi_status status = napi_get_value_string_utf8(env, in, buff, STRING_MAX_SIZE, &length);
    if (status != napi_ok || length == 0) {
        SHARING_LOGE("error invalid input string.");
        return false;
    }

    out = std::string(buff);

    return true;
}

} // namespace Sharing
} // namespace OHOS